import { debounce, isEmpty, omitBy } from "lodash-es";
import { CSSProperties } from "react";
import { create, StateCreator } from "zustand";
import { persist } from "zustand/middleware";
import { TreeDataNode } from "antd";
export interface ThemeCssType {
  cooptedStyle?: Record<string, any>;
}
export interface Component {
  id: string;
  name: string;
  innerName?: string; //在特定容器内部的名称
  props: any;
  // styles?: CSSProperties;
  styles?: Record<string, any>;
  desc: string;
  alias: string;
  body?: Component[];
  aside?: Component[];
  header?: Component[];
  footer?: Component[];
  buttonGroup?: Component[];
  parentId?: string;
  title?: string; //= Page.label
  definitions?: Component[];
  themeCss?: ThemeCssType;
  customClassStyle?: Record<string, any>;
}
export interface Page {
  id: number;
  icon: string;
  label: string;
  path: string;
  schema: Component;
  // [key: string]: any;
}

/**
  添加类型时
  getComponentById<注意cArr&arr>/getComponentUnitType 方法,
  类型Component, 
  dev和prod的渲染函数也要相应修改
  checkComponentsAllValid方法
*/
export type ComponentUnitType = "body" | "footer" | "header" | "buttonGroup" | "aside" | "definitions"
interface State {
  curComponentId: string | null;
  curComponent: Component | null;
  curComponentUnitType: ComponentUnitType | undefined;
  pages: Array<Page>;
  schema: Component;
  preview: boolean;
  popEditing: boolean; //判断是否编辑弹窗
  curPopTarget: Component | null;
  contextProperties:{ //组件上下文属性
    [key:string]: TreeDataNode
  }
}
interface Actions {
  setCurComponentId: (componentId: string | null) => void;
  setCurComponentUnitType: (type: ComponentUnitType|undefined) => void;
  addComponent: (component: Component, parentId: string, type?: ComponentUnitType) => void;
  deleteComponent: (componentId: string, type?: ComponentUnitType) => void;
  clearComponentSubitems: (componentId: string, type?: ComponentUnitType) => void;
  updateFullComponent: (component: Component) => void;
  updateComponentProps: (componentId: string, props: any) => void;
  updateComponentAlias: (componentId: string, alias: string) => void;
  updateComponentStyles: (componentId: string, styleObj: CSSProperties) => void;
  updateComponentCooptedStyles: (
    componentId: string,
    tKey: keyof ThemeCssType,
    styles: Partial<ThemeCssType["cooptedStyle"]>
  ) => void;
  setMode: (mode: State["preview"]) => void;
  addComponentEvent: (componentId: string | null, eventName: string) => void;
  removeComponentEvent: (componentId: string | null, eventName: string) => void;
  updateComponentCustomStyle: (componentId: string, customClassStyle: Record<string, any>) => void;
  addPage: (path: string, schema: string, id: number, icon: string, label: string) => void;
  updatePage: (path: string, schema: string) => void;
  delPage: (path: string) => void,
  setCurSchema: (schema: Component) => void;
  setPopEditing: (val: boolean) => void;
  setCurPopTarget: (popId: string | null) => void;
  setContextProperties: (id: string, properties: TreeDataNode)=>void
}

export const initSchema = (alias: string="页面")=>({
  id: "$s1",
  title: "Hello World",
  name: "Page",
  desc: "页面",
  alias,
  props: {
    showAside: false,
  },
  body: [],
  definitions: [],
});
const creator: StateCreator<State & Actions> = (set, get) => ({
  popEditing: false,
  curComponentId: null,
  curComponent: null,
  curComponentUnitType: undefined,
  curPopTarget: null,
  pages: [],
  schema: initSchema(),
  preview: false,
  contextProperties:{},
  setCurComponentId: componentId => {
    set(state => ({
      curComponentId: componentId,
      curComponent: getComponentById(componentId, state.schema),
    }));
  },
  setCurPopTarget: popId => {
    set({ curPopTarget: getComponentById(popId, get().schema) });
  },
  setCurComponentUnitType: (type) => {
    set({ curComponentUnitType: type });
  },
  addComponent: (component, parentId, type) => {
    type = type || "body";
    set(state => {
      if (parentId) {
        const parentComponent = getComponentById(parentId, state.schema);
        if (parentComponent) {
          if (Array.isArray(parentComponent[type])) {
            parentComponent[type]!.push(component);
          } else {
            parentComponent[type] = [component];
          }
        }
        component.parentId = parentId;
        const curComponent = getComponentById(get().curComponentId, get().schema)
        return {
          schema: { ...state.schema },
          curComponent,
          pages: getUpdatePages(state)
        }
      }
      return {};
    });
  },
  deleteComponent: (componentId, type) => {
    if (!componentId) return;
    const component = getComponentById(componentId, get().schema);
    type = type || getComponentUnitType(get().curComponent, get().schema)
    if (component?.parentId && type) {
      const parentComponent = getComponentById(component.parentId, get().schema);
      if (parentComponent) {
        parentComponent[type] = parentComponent[type]!.filter(c => c.id !== componentId);
        const curComponent = getComponentById(get().curComponentId, get().schema)
        set({ schema: { ...get().schema }, curComponent, pages: getUpdatePages(get()) });
      }
    }
  },
  clearComponentSubitems: (componentId, type) => {
    type = type || "body";
    if (!componentId) return;
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component[type] = [];
      set({ schema: { ...get().schema },curComponent: component, pages: getUpdatePages(get()) });
    }
  },
  updateFullComponent: newComponent => {
    set(state => {
      const { id, parentId } = newComponent;
      if (parentId) {
        const parentComponent = getComponentById(parentId, state.schema);
        let oldComponent
        if (parentComponent?.body) {
          const index = parentComponent.body.findIndex(c => c.id === id);
          if (index > -1) {
            oldComponent = parentComponent.body[index];
            parentComponent.body[index] = {
              ...newComponent,
              id: oldComponent.id,
              name: oldComponent.name,
              parentId: oldComponent.parentId,
              desc: oldComponent.desc,
            };
          }
        }
        if (parentComponent?.footer) {
          const index = parentComponent.footer.findIndex(c => c.id === id);
          if (index > -1) {
            oldComponent = parentComponent.footer[index];
            parentComponent.footer[index] = {
              ...newComponent,
              id: oldComponent.id,
              name: oldComponent.name,
              parentId: oldComponent.parentId,
              desc: oldComponent.desc,
            };
          }
        }
        if (parentComponent?.header) {
          const index = parentComponent.header.findIndex(c => c.id === id);
          if (index > -1) {
            oldComponent = parentComponent.header[index];
            parentComponent.header[index] = {
              ...newComponent,
              id: oldComponent.id,
              name: oldComponent.name,
              parentId: oldComponent.parentId,
              desc: oldComponent.desc,
            };
          }
        }
        if (parentComponent?.definitions) {
          const index = parentComponent.definitions.findIndex(c => c.id === id);
          if (index > -1) {
            oldComponent = parentComponent.definitions[index];
            parentComponent.definitions[index] = {
              ...newComponent,
              id: oldComponent.id,
              name: oldComponent.name,
              parentId: oldComponent.parentId,
              desc: oldComponent.desc,
            };
          }
        }
        if (parentComponent?.buttonGroup) {
          const index = parentComponent.buttonGroup.findIndex(c => c.id === id);
          if (index > -1) {
            oldComponent = parentComponent.buttonGroup[index];
            parentComponent.buttonGroup[index] = {
              ...newComponent,
              id: oldComponent.id,
              name: oldComponent.name,
              parentId: oldComponent.parentId,
              desc: oldComponent.desc,
            };
          }
        }
        
        return oldComponent?{
          schema: { ...state.schema },
          curComponent:{
            ...newComponent,
            id: oldComponent.id,
            name: oldComponent.name,
            parentId: oldComponent.parentId,
            desc: oldComponent.desc,
          },
          pages: getUpdatePages(get())
        }:{}
      } else {
        state.schema = {
          ...newComponent,
          id: state.schema.id,
          name: state.schema.name,
          desc: state.schema.desc,
        };
        return { schema: { ...state.schema },
        curComponent:{...state.schema},
        pages: getUpdatePages(get()) };
      }
    });
  },
  updateComponentProps: (componentId, props) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component.props = { ...component.props, ...props };
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },
  updateComponentAlias: (componentId, alias) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component.alias = alias;
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },
  updateComponentStyles: (componentId, styleObj) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component.styles = omitBy({ ...component.styles, ...styleObj }, value =>
        ["", null, undefined].includes(value as any)
      );
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get()) 
      })
    }
  },
  updateComponentCooptedStyles: (componentId, tKey, styles) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      /*初始化，无则增*/
      if (!component?.themeCss) {
        component.themeCss = {};
      }
      component.themeCss[tKey] = omitBy({ ...component.themeCss[tKey], ...styles }, value =>
        ["", null, undefined].includes(value)
      );
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },
  setMode: preview => {
    set({ preview });
  },
  addComponentEvent: (componentId, eventName) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component.props = { ...component.props, [eventName]: {} };
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },
  removeComponentEvent: (componentId, eventName) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      delete component.props[eventName];
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },

  updateComponentCustomStyle: (componentId, style) => {
    const component = getComponentById(componentId, get().schema);
    if (component) {
      component.customClassStyle = { ...style };
      set({
        schema: { ...get().schema },
        curComponent: component,
        pages: getUpdatePages(get())
      })
    }
  },
  addPage: (path: string, schema: string, id: number, icon: string, label: string) => {
    set(state => {
      let newSchema: Component = schema === "null" ? initSchema(label) : (JSON.parse(schema) as unknown as Component);
      state.setCurSchema(newSchema);
      const safePages = state.pages.filter(item => item.path !== path || (item.path===path && item.id === id))//把path相等，id不等的废弃数据去掉
      return {
        pages: [...safePages, { path, schema: newSchema, id, icon, label }],
      };
    });
  },
  updatePage: (path: string, schema: string) => {
    set(state => {
      return {
        pages: state.pages.map(item => {
          if (item.path === path) {
            schema === "null" ? (item.schema = initSchema(item.label)) : (item.schema = JSON.parse(schema) as unknown as Component);
            return item;
          }
          return item;
        }),
      };
    });
  },
  delPage: (path: string) => {
    const newPages = get().pages.filter(item => item.path !== path);
    set({ pages: newPages });
  },
  setCurSchema: (schema: Component) => {
    set({ schema, curComponentId: schema.id, curComponent: schema });
  },
  setPopEditing: (popEditing: boolean) => set({ popEditing }),
  setContextProperties: (id: string, properties: TreeDataNode) => {
    set({contextProperties:{...get().contextProperties, id: properties}})
  }
});

function getUpdatePages(state: State){
  const path = new URLSearchParams(window.location.search).get("path") || "unknown";
  const schema = state.schema
  return state.pages.map(item => {
    if (item.path === path) {
      schema === null ? (item.schema = initSchema(item.label)) : (item.schema = schema );
      return item;
    }
    return item;
  })
}

export function getComponentById(id: string | null | undefined, schema: Component): Component | null {
  if (!id) return null;
  if (schema.id === id) return schema;
  const loop = (id: string, components: Component[]): Component | null => {
    for (const component of components) {
      if (component.id == id) return component;
      const cArr = (component.body || [])
      .concat(component.aside || [])
      .concat(component.footer || [])
      .concat(component.header || [])
      .concat(component.buttonGroup || [])

      if (cArr && cArr.length > 0) {
        const result = loop(id, cArr);
        if (result !== null) return result;
      }
    }
    return null;
  };
  const arr = (schema.body || [])
  .concat(schema.aside || []).
  concat(schema.definitions || []);
  return loop(id, arr);
}
export function getParentTypeComponent(parentId: string | null | undefined, type: string | string[], schema: Component){
  if(!parentId)return null
  const component = getComponentById(parentId, schema);
  const typeArr: string[] = []
  typeArr.concat(type)
  if(!component)return null
  if(typeArr.includes(component.name)){
    return getParentTypeComponent(component.parentId, type, schema)
  }else{
    return component;
  }
}
export function getComponentUnitType(component:Component|null, schema: Component){
  if(!component) return
  const {parentId, id} = component
  if(!parentId) return
  const pComponent = getComponentById(parentId,schema)
  if(!pComponent)return
  const body = pComponent.body || []
  const aside = pComponent.aside || []
  const header = pComponent.header || []
  const footer = pComponent.footer || []
  const buttonGroup = pComponent.buttonGroup || []
  if(body.some(cell=>cell.id === id))return "body"
  if(aside.some(cell=>cell.id === id))return "aside"
  if(header.some(cell=>cell.id === id))return "header"
  if(footer.some(cell=>cell.id === id))return "footer"
  if(buttonGroup.some(cell=>cell.id === id))return "buttonGroup"
}
export const useComponentsStore = create<State & Actions>()(
  persist(creator, {
    name: "edit-pages-store",
  })
);
